import express from 'express'
import cors from 'cors'
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'
import fs from 'fs-extra'
import { v4 as uuidv4 } from 'uuid'
import { NodeSSH } from 'node-ssh'
import simpleGit from 'simple-git'
import archiver from 'archiver'
import extract from 'extract-zip'
import dotenv from 'dotenv'

dotenv.config()

const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)

const app = express()
const PORT = process.env.PORT || 3001

// 中间件
app.use(cors())
app.use(express.json())

// 数据存储路径
const DATA_DIR = join(__dirname, 'data')
const DEPLOYMENTS_FILE = join(DATA_DIR, 'deployments.json')
const SERVER_CONFIG_FILE = join(DATA_DIR, 'server-config.json')
const LOGS_FILE = join(DATA_DIR, 'logs.json')
const PROJECT_LOGS_DIR = join(DATA_DIR, 'project-logs')

// 确保数据目录存在
fs.ensureDirSync(DATA_DIR)
fs.ensureDirSync(PROJECT_LOGS_DIR)

// 初始化数据文件
const initDataFiles = () => {
  if (!fs.existsSync(DEPLOYMENTS_FILE)) {
    fs.writeJsonSync(DEPLOYMENTS_FILE, [])
  }
  if (!fs.existsSync(LOGS_FILE)) {
    fs.writeJsonSync(LOGS_FILE, [])
  }
}

initDataFiles()

// 日志记录
const addLog = (message, type = 'info', projectId = null) => {
  const logs = fs.readJsonSync(LOGS_FILE)
  const log = {
    id: uuidv4(),
    message,
    type,
    timestamp: new Date().toISOString(),
    projectId
  }
  logs.unshift(log)
  // 只保留最近100条日志
  if (logs.length > 100) {
    logs.splice(100)
  }
  fs.writeJsonSync(LOGS_FILE, logs)
  
  // 如果指定了项目ID，同时记录到项目日志
  if (projectId) {
    addProjectLog(projectId, message, type)
  }
  
  return log
}

// 项目级别日志记录
const addProjectLog = (projectId, message, type = 'info') => {
  const projectLogFile = join(PROJECT_LOGS_DIR, `${projectId}.json`)
  
  let projectLogs = []
  if (fs.existsSync(projectLogFile)) {
    projectLogs = fs.readJsonSync(projectLogFile)
  }
  
  const log = {
    id: uuidv4(),
    message,
    type,
    timestamp: new Date().toISOString()
  }
  
  projectLogs.unshift(log)
  // 只保留最近200条项目日志
  if (projectLogs.length > 200) {
    projectLogs.splice(200)
  }
  
  fs.writeJsonSync(projectLogFile, projectLogs)
  return log
}

// 服务器配置相关API
app.get('/api/server/config', (req, res) => {
  try {
    if (fs.existsSync(SERVER_CONFIG_FILE)) {
      const config = fs.readJsonSync(SERVER_CONFIG_FILE)
      res.json(config)
    } else {
      res.status(404).json({ error: '配置不存在' })
    }
  } catch (error) {
    res.status(500).json({ error: '读取配置失败' })
  }
})

app.post('/api/server/config', (req, res) => {
  try {
    const config = req.body
    fs.writeJsonSync(SERVER_CONFIG_FILE, config)
    addLog('服务器配置已保存')
    res.json(config)
  } catch (error) {
    res.status(500).json({ error: '保存配置失败' })
  }
})

app.post('/api/server/test-connection', async (req, res) => {
  try {
    const config = req.body
    const ssh = new NodeSSH()
    
    const sshConfig = {
      host: config.host,
      port: config.port,
      username: config.username
    }
    
    if (config.authType === 'password') {
      sshConfig.password = config.password
    } else {
      sshConfig.privateKeyPath = config.privateKeyPath
      if (config.passphrase) {
        sshConfig.passphrase = config.passphrase
      }
    }
    
    await ssh.connect(sshConfig)
    ssh.dispose()
    
    addLog(`服务器连接测试成功: ${config.host}`)
    res.json({ success: true })
  } catch (error) {
    addLog(`服务器连接测试失败: ${error.message}`, 'error')
    res.status(500).json({ error: '连接失败', details: error.message })
  }
})

// 部署相关API
app.get('/api/deployments', (req, res) => {
  try {
    const deployments = fs.readJsonSync(DEPLOYMENTS_FILE)
    res.json(deployments)
  } catch (error) {
    res.status(500).json({ error: '获取部署列表失败' })
  }
})

app.post('/api/deployments', (req, res) => {
  try {
    const deployment = {
      id: uuidv4(),
      ...req.body,
      status: 'pending',
      createdAt: new Date().toISOString(),
      updatedAt: new Date().toISOString()
    }
    
    const deployments = fs.readJsonSync(DEPLOYMENTS_FILE)
    deployments.push(deployment)
    fs.writeJsonSync(DEPLOYMENTS_FILE, deployments)
    
    addLog(`创建部署配置: ${deployment.projectName}`, 'info', deployment.id)
    res.json(deployment)
  } catch (error) {
    res.status(500).json({ error: '创建部署失败' })
  }
})

app.put('/api/deployments/:id', (req, res) => {
  try {
    const { id } = req.params
    const deployments = fs.readJsonSync(DEPLOYMENTS_FILE)
    const deploymentIndex = deployments.findIndex(d => d.id === id)
    
    if (deploymentIndex === -1) {
      return res.status(404).json({ error: '部署配置不存在' })
    }
    
    const updatedDeployment = {
      ...deployments[deploymentIndex],
      ...req.body,
      id, // 保持原有ID
      createdAt: deployments[deploymentIndex].createdAt, // 保持创建时间
      updatedAt: new Date().toISOString()
    }
    
    deployments[deploymentIndex] = updatedDeployment
    fs.writeJsonSync(DEPLOYMENTS_FILE, deployments)
    
    addLog(`更新部署配置: ${updatedDeployment.projectName}`, 'info', updatedDeployment.id)
    res.json(updatedDeployment)
  } catch (error) {
    res.status(500).json({ error: '更新部署失败' })
  }
})

app.post('/api/deployments/:id/deploy', async (req, res) => {
  const { id } = req.params
  
  try {
    const deployments = fs.readJsonSync(DEPLOYMENTS_FILE)
    const deployment = deployments.find(d => d.id === id)
    
    if (!deployment) {
      return res.status(404).json({ error: '部署配置不存在' })
    }
    
    // 更新状态为部署中
    deployment.status = 'deploying'
    deployment.updatedAt = new Date().toISOString()
    fs.writeJsonSync(DEPLOYMENTS_FILE, deployments)
    
    addLog(`开始部署项目: ${deployment.projectName}`, 'info', deployment.id)
    
    // 异步执行部署
    deployProject(deployment).catch(error => {
      addLog(`部署失败: ${deployment.projectName} - ${error.message}`, 'error', deployment.id)
      
      // 更新状态为失败
      const updatedDeployments = fs.readJsonSync(DEPLOYMENTS_FILE)
      const updatedDeployment = updatedDeployments.find(d => d.id === id)
      if (updatedDeployment) {
        updatedDeployment.status = 'failed'
        updatedDeployment.updatedAt = new Date().toISOString()
        fs.writeJsonSync(DEPLOYMENTS_FILE, updatedDeployments)
      }
    })
    
    res.json({ success: true, message: '部署任务已启动' })
  } catch (error) {
    res.status(500).json({ error: '启动部署失败' })
  }
})

app.delete('/api/deployments/:id', (req, res) => {
  try {
    const { id } = req.params
    const deployments = fs.readJsonSync(DEPLOYMENTS_FILE)
    const filteredDeployments = deployments.filter(d => d.id !== id)
    fs.writeJsonSync(DEPLOYMENTS_FILE, filteredDeployments)
    
    addLog(`删除部署配置: ${id}`, 'info')
    res.json({ success: true })
  } catch (error) {
    res.status(500).json({ error: '删除部署失败' })
  }
})

app.get('/api/deployments/stats', (req, res) => {
  try {
    const deployments = fs.readJsonSync(DEPLOYMENTS_FILE)
    const stats = {
      total: deployments.length,
      success: deployments.filter(d => d.status === 'success').length,
      failed: deployments.filter(d => d.status === 'failed').length
    }
    res.json(stats)
  } catch (error) {
    res.status(500).json({ error: '获取统计信息失败' })
  }
})

app.get('/api/deployments/logs', (req, res) => {
  try {
    const logs = fs.readJsonSync(LOGS_FILE)
    res.json(logs.slice(0, 20)) // 返回最近20条日志
  } catch (error) {
    res.status(500).json({ error: '获取日志失败' })
  }
})

// 获取项目日志
app.get('/api/deployments/:id/logs', (req, res) => {
  try {
    const { id } = req.params
    const { limit = 50 } = req.query
    
    const projectLogFile = join(PROJECT_LOGS_DIR, `${id}.json`)
    
    if (!fs.existsSync(projectLogFile)) {
      return res.json([])
    }
    
    const logs = fs.readJsonSync(projectLogFile)
    const limitedLogs = logs.slice(0, parseInt(limit))
    
    res.json(limitedLogs)
  } catch (error) {
    res.status(500).json({ error: '获取项目日志失败' })
  }
})

// 获取部署历史
app.get('/api/deployments/:id/history', (req, res) => {
  try {
    const { id } = req.params
    const deployments = fs.readJsonSync(DEPLOYMENTS_FILE)
    const deployment = deployments.find(d => d.id === id)
    
    if (!deployment) {
      return res.status(404).json({ error: '部署配置不存在' })
    }
    
    const history = deployment.deployHistory || []
    res.json(history)
  } catch (error) {
    res.status(500).json({ error: '获取部署历史失败' })
  }
})

// 查看本地部署文件
app.get('/api/deployments/:id/files', (req, res) => {
  try {
    const { id } = req.params
    const deployments = fs.readJsonSync(DEPLOYMENTS_FILE)
    const deployment = deployments.find(d => d.id === id)
    
    if (!deployment) {
      return res.status(404).json({ error: '部署配置不存在' })
    }
    
    if (!deployment.localDeployPath) {
      return res.status(404).json({ error: '本地部署路径不存在' })
    }
    
    const files = []
    const scanDirectory = (dir, relativePath = '') => {
      const items = fs.readdirSync(dir)
      for (const item of items) {
        const fullPath = join(dir, item)
        const relativeItemPath = relativePath ? join(relativePath, item) : item
        const stat = fs.statSync(fullPath)
        
        if (stat.isDirectory()) {
          files.push({
            name: item,
            path: relativeItemPath,
            type: 'directory',
            size: null
          })
          scanDirectory(fullPath, relativeItemPath)
        } else {
          files.push({
            name: item,
            path: relativeItemPath,
            type: 'file',
            size: stat.size
          })
        }
      }
    }
    
    scanDirectory(deployment.localDeployPath)
    res.json({ files, deployPath: deployment.localDeployPath })
  } catch (error) {
    res.status(500).json({ error: '获取文件列表失败' })
  }
})

// 部署项目的主要逻辑
async function deployProject(deployment) {
  const tempDir = join(__dirname, 'temp', deployment.id)
  
  try {
    // 检查是否为本地部署模式
    const isLocalDeploy = !fs.existsSync(SERVER_CONFIG_FILE) || 
                         (fs.existsSync(SERVER_CONFIG_FILE) && 
                          fs.readJsonSync(SERVER_CONFIG_FILE).deployMode === 'local')
    
    if (isLocalDeploy) {
      await deployToLocal(deployment, tempDir)
    } else {
      await deployToRemote(deployment, tempDir)
    }
    
  } catch (error) {
    addLog(`部署失败: ${deployment.projectName} - ${error.message}`, 'error')
    throw error
  } finally {
    // 清理临时文件
    if (fs.existsSync(tempDir)) {
      fs.removeSync(tempDir)
    }
  }
}

// 本地部署逻辑
async function deployToLocal(deployment, tempDir) {
  try {
    addLog(`开始本地部署: ${deployment.projectName}`, 'info', deployment.id)
    
    // 创建临时目录
    fs.ensureDirSync(tempDir)
    
    // 克隆代码
    addLog(`开始克隆代码: ${deployment.repoUrl}`, 'info', deployment.id)
    const git = simpleGit(tempDir)
    await git.clone(deployment.repoUrl, tempDir)
    await git.checkout(deployment.branch)
    addLog(`代码克隆完成，分支: ${deployment.branch}`, 'info', deployment.id)
    
    // 执行部署前命令
    if (deployment.preDeployCommand) {
      addLog(`执行部署前命令: ${deployment.preDeployCommand}`, 'info', deployment.id)
      const { exec } = await import('child_process')
      const { promisify } = await import('util')
      const execAsync = promisify(exec)
      
      try {
        await execAsync(deployment.preDeployCommand, { cwd: tempDir })
        addLog('部署前命令执行完成', 'info', deployment.id)
      } catch (error) {
        addLog(`部署前命令执行失败: ${error.message}`, 'warning', deployment.id)
      }
    }
    
    // 执行构建命令
    addLog(`执行构建命令: ${deployment.buildCommand}`, 'info', deployment.id)
    const { exec } = await import('child_process')
    const { promisify } = await import('util')
    const execAsync = promisify(exec)
    
    try {
      const { stdout, stderr } = await execAsync(deployment.buildCommand, { cwd: tempDir })
      if (stderr) {
        addLog(`构建警告: ${stderr}`, 'warning', deployment.id)
      }
      addLog('构建完成', 'info', deployment.id)
    } catch (error) {
      throw new Error(`构建失败: ${error.message}`)
    }
    
    // 检查构建产物
    const distPath = join(tempDir, 'dist')
    if (!fs.existsSync(distPath)) {
      throw new Error('构建产物不存在，请检查构建命令')
    }
    
    // 创建本地部署目录（带时间戳避免覆盖）
    const timestamp = new Date().toISOString().replace(/[:.]/g, '-')
    const deployDirName = `${deployment.id}-${timestamp}`
    const localDeployPath = join(__dirname, 'deployments', deployDirName)
    fs.ensureDirSync(localDeployPath)
    
    // 复制构建产物到部署目录
    fs.copySync(distPath, localDeployPath, { overwrite: true })
    addLog(`文件部署完成: ${localDeployPath}`, 'info', deployment.id)
    
    // 执行部署后命令
    if (deployment.postDeployCommand) {
      addLog(`执行部署后命令: ${deployment.postDeployCommand}`, 'info', deployment.id)
      try {
        await execAsync(deployment.postDeployCommand, { cwd: localDeployPath })
        addLog('部署后命令执行完成', 'info', deployment.id)
      } catch (error) {
        addLog(`部署后命令执行失败: ${error.message}`, 'warning', deployment.id)
      }
    }
    
    // 更新状态为成功
    const deployments = fs.readJsonSync(DEPLOYMENTS_FILE)
    const updatedDeployment = deployments.find(d => d.id === deployment.id)
    if (updatedDeployment) {
      // 初始化部署历史数组
      if (!updatedDeployment.deployHistory) {
        updatedDeployment.deployHistory = []
      }
      
      // 添加新的部署记录
      const deployRecord = {
        id: uuidv4(),
        timestamp: new Date().toISOString(),
        status: 'success',
        localDeployPath: localDeployPath,
        buildCommand: deployment.buildCommand,
        branch: deployment.branch
      }
      
      updatedDeployment.deployHistory.unshift(deployRecord)
      
      // 只保留最近10次部署记录
      if (updatedDeployment.deployHistory.length > 10) {
        updatedDeployment.deployHistory = updatedDeployment.deployHistory.slice(0, 10)
      }
      
      // 更新当前状态
      updatedDeployment.status = 'success'
      updatedDeployment.updatedAt = new Date().toISOString()
      updatedDeployment.currentDeployPath = localDeployPath
      
      fs.writeJsonSync(DEPLOYMENTS_FILE, deployments)
    }
    
    addLog(`本地部署成功: ${deployment.projectName}`, 'info', deployment.id)
    
  } catch (error) {
    addLog(`本地部署失败: ${deployment.projectName} - ${error.message}`, 'error', deployment.id)
    throw error
  }
}

// 远程部署逻辑（保留原有功能）
async function deployToRemote(deployment, tempDir) {
  const ssh = new NodeSSH()
  
  try {
    // 读取服务器配置
    const serverConfig = fs.readJsonSync(SERVER_CONFIG_FILE)
    
    // 连接服务器
    const sshConfig = {
      host: serverConfig.host,
      port: serverConfig.port,
      username: serverConfig.username
    }
    
    if (serverConfig.authType === 'password') {
      sshConfig.password = serverConfig.password
    } else {
      sshConfig.privateKeyPath = serverConfig.privateKeyPath
      if (serverConfig.passphrase) {
        sshConfig.passphrase = serverConfig.passphrase
      }
    }
    
    await ssh.connect(sshConfig)
    addLog(`已连接到服务器: ${serverConfig.host}`, 'info', deployment.id)
    
    // 创建临时目录
    fs.ensureDirSync(tempDir)
    
    // 克隆代码
    addLog(`开始克隆代码: ${deployment.repoUrl}`, 'info', deployment.id)
    const git = simpleGit(tempDir)
    await git.clone(deployment.repoUrl, tempDir)
    await git.checkout(deployment.branch)
    addLog(`代码克隆完成，分支: ${deployment.branch}`, 'info', deployment.id)
    
    // 执行部署前命令
    if (deployment.preDeployCommand) {
      addLog(`执行部署前命令: ${deployment.preDeployCommand}`, 'info', deployment.id)
      await ssh.execCommand(deployment.preDeployCommand, { cwd: tempDir })
    }
    
    // 执行构建命令
    addLog(`执行构建命令: ${deployment.buildCommand}`, 'info', deployment.id)
    const buildResult = await ssh.execCommand(deployment.buildCommand, { cwd: tempDir })
    if (buildResult.stderr) {
      throw new Error(`构建失败: ${buildResult.stderr}`)
    }
    addLog('构建完成', 'info', deployment.id)
    
    // 创建部署包
    const distPath = join(tempDir, 'dist')
    if (!fs.existsSync(distPath)) {
      throw new Error('构建产物不存在，请检查构建命令')
    }
    
    const zipPath = join(tempDir, 'deploy.zip')
    await createZipArchive(distPath, zipPath)
    addLog('部署包创建完成', 'info', deployment.id)
    
    // 上传到服务器
    await ssh.putFile(zipPath, '/tmp/deploy.zip')
    addLog('部署包上传完成', 'info', deployment.id)
    
    // 解压到目标目录
    await ssh.execCommand(`rm -rf ${deployment.deployPath}`)
    await ssh.execCommand(`mkdir -p ${deployment.deployPath}`)
    await ssh.execCommand(`unzip -o /tmp/deploy.zip -d ${deployment.deployPath}`)
    await ssh.execCommand(`rm /tmp/deploy.zip`)
    addLog(`文件部署完成: ${deployment.deployPath}`, 'info', deployment.id)
    
    // 执行部署后命令
    if (deployment.postDeployCommand) {
      addLog(`执行部署后命令: ${deployment.postDeployCommand}`, 'info', deployment.id)
      await ssh.execCommand(deployment.postDeployCommand, { cwd: deployment.deployPath })
    }
    
    // 更新状态为成功
    const deployments = fs.readJsonSync(DEPLOYMENTS_FILE)
    const updatedDeployment = deployments.find(d => d.id === deployment.id)
    if (updatedDeployment) {
      // 初始化部署历史数组
      if (!updatedDeployment.deployHistory) {
        updatedDeployment.deployHistory = []
      }
      
      // 添加新的部署记录
      const deployRecord = {
        id: uuidv4(),
        timestamp: new Date().toISOString(),
        status: 'success',
        deployPath: deployment.deployPath,
        buildCommand: deployment.buildCommand,
        branch: deployment.branch
      }
      
      updatedDeployment.deployHistory.unshift(deployRecord)
      
      // 只保留最近10次部署记录
      if (updatedDeployment.deployHistory.length > 10) {
        updatedDeployment.deployHistory = updatedDeployment.deployHistory.slice(0, 10)
      }
      
      // 更新当前状态
      updatedDeployment.status = 'success'
      updatedDeployment.updatedAt = new Date().toISOString()
      
      fs.writeJsonSync(DEPLOYMENTS_FILE, deployments)
    }
    
    addLog(`远程部署成功: ${deployment.projectName}`, 'info', deployment.id)
    
  } catch (error) {
    addLog(`远程部署失败: ${deployment.projectName} - ${error.message}`, 'error', deployment.id)
    throw error
  } finally {
    ssh.dispose()
  }
}

// 创建ZIP压缩包
function createZipArchive(sourcePath, outputPath) {
  return new Promise((resolve, reject) => {
    const output = fs.createWriteStream(outputPath)
    const archive = archiver('zip', { zlib: { level: 9 } })
    
    output.on('close', () => resolve())
    archive.on('error', (err) => reject(err))
    
    archive.pipe(output)
    archive.directory(sourcePath, false)
    archive.finalize()
  })
}

app.listen(PORT, () => {
  console.log(`服务器运行在 http://localhost:${PORT}`)
  addLog(`部署平台服务启动，端口: ${PORT}`)
})
